#include "Logger.h"
#include <iostream>
#include <exception>
#include <ctime>

const unsigned MAX_PID = 10; // Define el maximo tamaño de PID
const unsigned MAX_NAME = 12; // Define el maximo tamaño de nombre de proceso

/** Namespace con funciones auxiliares */
namespace {
/** Devuelve la hora local en cadena de tamaño fijo. Ej : Sun Apr 27 15:19:37 2014 */
std::string get_local_time() {
	time_t rawtime;
	struct tm * timeinfo;
	time(&rawtime);
	timeinfo = localtime(&rawtime);
	std::string time_format(asctime(timeinfo));
	time_format.resize(24); // Quita el '\n'
	return time_format;
}
/** Devuelve la cantidad de blancos para llenar segun el campo */
std::string get_blanks(int total, std::string field) {
	int amount_blanks = total - field.size();
	int i = 0;
	std::string blanks;
	while (i < amount_blanks) {
		blanks += " ";
		++i;
	}
	return blanks;
}
}

Logger::Logger(const std::string& file_name, const std::string& name_process,
		bool activate) :
		activate_(activate) {
	this->file_name_ = file_name;
	this->name_process_ = name_process;

	this->fl_.l_type = F_WRLCK;
	this->fl_.l_whence = SEEK_SET;
	this->fl_.l_start = 0;
	this->fl_.l_len = 0;
	this->fl_.l_pid = getpid();
	this->fd_ = open(this->file_name_.c_str(), O_CREAT | O_WRONLY, 0777);
	if (fd_ == 0) {
		//No se pudo abrir archivo (NOTE: Atrapar desde afuera)
		throw std::exception();
	}
}

std::string Logger::get_mode_text(LOG_MODE::Log_Values mode) {
	std::string result("");
	switch (mode) {
	case LOG_MODE::ERROR:
		result = "ERROR";
		break;
	case LOG_MODE::WARN:
		result = "WARN ";
		break;
	case LOG_MODE::INFO:
		result = "INFO ";
		break;
	default:
		break;
	}
	return result;
}

std::string Logger::convert_text(LOG_MODE::Log_Values mode,
		const std::string& text) {
	std::string time_format = get_local_time();
	std::stringstream ss;
	ss << getpid();
	std::string line_log = time_format + " [" + this->get_mode_text(mode) + "] "
			+ get_blanks(MAX_PID, ss.str()) + ss.str() + " ["
			+ this->name_process_ + "]"
			+ get_blanks(MAX_NAME, this->name_process_) + text + "\n";
	return line_log;
}

int Logger::write_text(LOG_MODE::Log_Values mode, const std::string& text) {
	if (this->activate_) {
		const std::string buffer = this->convert_text(mode, text);
		if (this->take_lock() != -1) {
			lseek(this->fd_, 0, SEEK_END);
			return write(this->fd_, buffer.c_str(), strlen(buffer.c_str()));
		}
	}
	return -1; // No se pudo escribir o NO esta activado el logger
}

int Logger::info(const std::string& text) {
	int result = this->write_text(LOG_MODE::INFO, text);
	this->leave_lock();
	return result;
}

int Logger::warn(const std::string& text) {
	int result = this->write_text(LOG_MODE::WARN, text);
	this->leave_lock();
	return result;
}

int Logger::error(const std::string& text) {
	int result = this->write_text(LOG_MODE::ERROR, text);
	this->leave_lock();
	return result;
}

Logger::~Logger() {
	close(this->fd_);
}

int Logger::take_lock() {
	this->fl_.l_type = F_WRLCK;
	return fcntl(this->fd_, F_SETLKW, &(this->fl_));
}
int Logger::leave_lock() {
	this->fl_.l_type = F_UNLCK;
	return fcntl(this->fd_, F_SETLK, &(this->fl_));

}

